package cn.flying.cloud.gateway.oauth.configuration.authorization;

import javax.annotation.Resource;

import java.text.ParseException;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;

import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.SignedJWT;
import net.minidev.json.JSONObject;
import reactor.core.publisher.Mono;

import cn.flying.cloud.gateway.oauth.configuration.authentication.WhiteListConfig;
import cn.flying.cloud.gateway.redis.service.RedisService;

/**
 * 鉴权管理器，用于判断是否有资源的访问权限
 *
 * @author: admin
 * @date: 2023年06月26日 10:18
 * @version: 1.0
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RedisService redisService;
    @Resource
    private WhiteListConfig whiteListConfig;

    /**
     * 取出令牌中的权限和当前请求资源URI的权限对比，如果有交集则通过
     *
     * @param mono
     * @param authorizationContext
     * @return
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();
        //获取请求的uri
        String path = request.getURI().getPath();
        logger.info("请求uri：【{}】", path);

        //1、option请求，对应跨域的预检请求直接放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }

        //2、白名单路径直接放行
        PathMatcher pathMatcher = new AntPathMatcher();
        List<String> whiteList = whiteListConfig.getUris();
        for (String ignoreUri : whiteList) {
            if (pathMatcher.match(ignoreUri, path)) {
                return Mono.just(new AuthorizationDecision(true));
            }
        }

        //3、token为空拒绝访问
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isBlank(token)) {
            return Mono.just(new AuthorizationDecision(false));
        }

        JWT jwt = null;
        try {
            jwt = JWTParser.parse(token.replace("Bearer ", ""));
        } catch (ParseException e) {
            logger.error("Token解析失败！", e);
            return Mono.just(new AuthorizationDecision(false));
        }
        //{
        //	"userInfo": {
        //		"tenantId": "10000",
        //		"avatar": "",
        //		"userName": "admin",
        //		"userId": "10000"
        //	},
        //	"user_name": "admin",
        //	"scope": ["all"],
        //	"exp": 1727456925,
        //	"authorities": ["sys:role:permit", "sys:res:del", "mss:template:edit"],
        //	"jti": "8ff533c2-e3c3-4d7c-8cb9-996f7e96e553",
        //	"client_id": "00a9e5af105b483f8ebf7046ee49cb0b"
        //}
        JSONObject object = ((SignedJWT) jwt).getPayload().toJSONObject();
        logger.info("解析Token数据：{}", object);
        JSONObject userInfo = (JSONObject) object.get("userInfo");
        Object tenantId = userInfo.get("tenantId");
        Object userId = userInfo.get("userId");

        //4、从Redis缓存中获取当前路径可访问角色列表（url级别访问控制）
//        Map<Object, Object> entries = redisService.redisTpl().opsForHash().entries(Constants.AUTH_PERMS + ":" + tenantId + ":" + userId);
//        Set<?> set = JacksonUtil.deserialize((String) o, Set.class);

        // 暂时全部通过
        return Mono.just(new AuthorizationDecision(true));

        //TODO

//        return mono.map(auth -> {
//            // 如果用户未认证，直接拒绝访问
//            if (!auth.isAuthenticated()) {
//                return new AuthorizationDecision(false);
//            }
//
//            // 获取用户拥有的权限
//            Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
//
//            // 检查用户是否有访问该资源的权限
//            // 1. 实际应用中，这里应该从权限配置中获取访问该资源所需的权限
//            // 2. 例如可以从数据库、缓存或配置文件中查询资源与权限的映射关系
//            String requiredPermission = "";
//
//            // 3. 检查用户是否拥有所需权限
//            if (requiredPermission == null) {
//                // 如果资源不需要特定权限，允许访问
//                new AuthorizationDecision(true);
//            }
//
//            boolean hasPermission = authorities.stream()
//                    .anyMatch(authority -> requiredPermission.equals(authority.getAuthority()));
//            return new AuthorizationDecision(hasPermission);
//        }).defaultIfEmpty(new AuthorizationDecision(false));

//
//        String obj = (String) redisService.redisTpl().opsForHash().get(GatewayConstant.RESOURCE_ROLES_MAP, path);
//        List<String> list = Arrays.asList(obj.split(","));
//
//        //5、请求路径匹配到的资源需要的角色权限集合authorities
//        List<String> authorities = list.stream().map(i -> i = "ROLE_" + i).collect(Collectors.toList());
//        //认证通过且角色匹配的用户可访问当前路径
//        return mono
//                //取认证成功的
//                .filter(Authentication::isAuthenticated)
//                //获取认证后的全部权限
//                .flatMapIterable(Authentication::getAuthorities)
//                .map(GrantedAuthority::getAuthority)
//                //判断权限列表是否包含权限，有交集，则放行
//                .any(authorities::contains)
//                .map(AuthorizationDecision::new)
//                .defaultIfEmpty(new AuthorizationDecision(false));
    }

}